﻿using System;
using System.Collections;
using System.Linq;
using System.Text;
using System.Collections.Generic;

namespace OrangePrj
{
    [Serializable]
    public class Project : ICloneable
    {
        #region Поля и свойства
        private string name;
        private string descr;
        public WorkList worklist;
        public List<Event> eventList;
        private int maxId;
        public string Name
        {
            get
            {
                return name;
            }
            set
            {
                name = value;
            }
        }
        public string Descr
        {
            get
            {
                return descr;
            }
            set
            {
                descr = value;
            }
        }
        public User user { get; private set; }


        #endregion

        #region Конструкторы
        public Project() : this (null, null, null)
        {
        }
        public Project(String name, String description, User user)
        {
            worklist = new WorkList();
            eventList = new List<Event>();
            this.Name = name;
            this.Descr = description;
            this.user = user;
        }
        public Project(Project project) : this(project.Name, project.Descr, project.user)
        {
            foreach (Work w in project.worklist)
            {
                Work tempWork = (Work)w.Clone();
                this.worklist.Add(tempWork);
            }
        }
        #endregion

        public double getPrjTime()
        {
            return worklist.getPrjTime();
        }
        public void setReverseDepWorks()
        {
            foreach (Work work in worklist)
            {
                foreach (Work w in work.DepWorks)
                {
                    w.ReversDepWorks.Add(work);
                }
            }
        }

        #region Проверка структуры проекта

        public string checkAll()
        {
            string error = string.Empty;
            setReverseDepWorks();
            error += checkWorkTypeXOR();
            error += checkIdenticalEvenets();
            //setEvents();
            return error;
        }

        public void setEvents()
        {
            bool check;
            int maxEvenetRank;
            Event.count = 0;
            WorkList tempWorkList = new WorkList();
            Event firstEvent = new Event(this);

            setReverseDepWorks();

            foreach (Work w in worklist)
            {
                check = true;
                if (w.DepWorks.Count == 0)
                {
                    w.beginEvent = firstEvent;
                }
                foreach (Work work in tempWorkList)
                {
                    if (compaireIndWorkList(w, work) || (w.DepWorks.Count == 0 && work.DepWorks.Count == 0))
                    {
                        w.beginEvent = work.beginEvent;
                        check = true;
                        break;
                    }
                    else
                    {
                        check = false;
                    }
                }
                if (!check)
                {
                    w.beginEvent = new Event(this);
                }
                tempWorkList.Add(w);
            }

            Event lastEvent = new Event(this);
            foreach (Work w in worklist)
            {
                if (w.ReversDepWorks.Count > 0)
                {
                    w.endEvent = w.ReversDepWorks[0].beginEvent;
                }
                else
                {
                    w.endEvent = lastEvent;
                }
            }

            foreach (Work w in worklist)
            {
                w.beginEvent.OutWork.Add(w);
                w.endEvent.InWork.Add(w);
            }

            foreach (Work w in worklist)
            {
                if (w.hasDepends() == false)
                    w.rank = 0;
                else
                    w.setMaxRank();
            }

            foreach (Event e in eventList)
            {
                foreach (Work w in e.InWork)
                {
                    if (e.Rank <= w.rank)
                    {
                        e.Rank = w.rank + 1;
                    }
                }
            }

            maxEvenetRank = this.worklist.getMaxRank() + 1;

        }

        /// <summary>
        /// Проверяет образовались ли циклы при установке зависимостей
        /// </summary>
        /// <param name="w">Работа для проверки</param>
        /// <param name="depWorks">Список зависимостей</param>
        /// <param name="count">Количество шагов пройденных в глубь. При первом запуске нужно устанавливать этот параметр равным 0</param>
        /// <returns>true - есть цикл. false - циклов нет</returns>
        public bool TreeBuild(Work w, WorkList depWorks, int count)
        {
            bool check;
            if (count == this.worklist.Count)
            {
                return false;
            }
            foreach (Work work in depWorks)
            {
                if (work.Equals(w))
                {
                    return true;
                }
            }
            foreach (Work work in depWorks)
            {
                count++;
                check = TreeBuild(w, work.DepWorks, count);
                if (check)
                {
                    return true;
                }
            }

            return false;
        }//хорошая получилась функция (с) Orange Development Team

        /// <summary>
        /// Проверка на наличие висячих событий
        /// </summary>
        /// <returns>Сообщение об ошибке</returns>
        public string checkBeginWork()                              
        {
            string error = string.Empty;
            int countBeginWork = 0;
            WorkList workBeginList = new WorkList();
            for (int i = 0; i < worklist.Count; i++)
            {
                if(worklist[i].DepWorks.Count == 0)
                {
                    countBeginWork++;
                    workBeginList.Add(worklist[i]);
                }
            }
            if (countBeginWork > 1)
            {
                error = "В проекте присутствует более одной начальной работы:\n";
                foreach (Work w in workBeginList)
                {
                    error += w.Name + "\n";
                }
                error += "\n";
            }
            return error;
        }
        public string checkEndWork()                                //проверка на наличие тупиковых событий
        {
            string error = "";

            WorkList workListDep = new WorkList();
            WorkList tempList = new WorkList();
            int countEndWork = 0;
            foreach (Work w in worklist)
            {
                foreach (Work workDep in w.DepWorks)
                {
                    workListDep.Add(workDep);
                }
            }

            foreach (Work w in worklist)
            {
                if (!workListDep.Contains(w) && w.Type != WorkType.Возврат)
                {
                    countEndWork++;
                    tempList.Add(w);
                }
                
            }

            if (countEndWork > 1)
            {
                error = "В проекте присутствует более одной конечной работы:\n";
                foreach (Work w in tempList)
                {
                    error += w.Name + "\n";
                }
                error += "\n";
            }
            
            return error;
        }

        public string checkWorkTypeXOR()
        {
            string error = "";
            WorkList tempList = new WorkList();
            WorkList pairList = new WorkList();
            double totalProbability = 0;

            foreach (Work w in worklist)
            {
                if (w.Type == WorkType.Искл_ИЛИ)
                {
                    tempList.Add(w);
                }
            }

            if (tempList.Count == 1)
            {
                return "Работа " + tempList[0].Name + " является работой типа Исключающее Или.\n Добавьте еще хотя бы одну работу, которая имеет такие же зависимости.";
            }
            for (int i = 0; i < tempList.Count - 1; i++ )
            {
                pairList.Clear();
                pairList.Add(tempList[i]);
                
                for (int j = i + 1; j < tempList.Count; j++ )
                {
                    if(compaireIndWorkList(tempList[i], tempList[j]))
                    {
                        pairList.Add(tempList[j]);
                        tempList.RemoveAt(j);
                        j--;
                    }
                }

                if (pairList.Count <= 1)
                {
                    return "Работа " + pairList[0].Name + " является работой типа Исключающее Или, но не имеет пары.\nДобавьте еще хотя бы одну работу, которая имеет такие же зависимости.";
                }

                tempList.RemoveAt(i);
                i--;

                foreach (Work w in pairList)
                {
                    totalProbability += w.Probability;
                }
                if(totalProbability != 1)
                {
                    error += "Следущие работы типа Исключающего Или выходят имеют общие зависимости,\nно сумма их вероятностей не равна 1:\n";
                    foreach (Work w in pairList)
                    {
                        error += w.Name + "\n";
                    }
                    
                }
                totalProbability = 0;
            }
                            
            return error;
        }                         //проверка для работ типа ИЛИ
        public string checkIdenticalEvenets()                       //проверка на наличие у работ одинаковых начальных и конечных событий
        {
            string error = "";
            for (int i = 0; i < worklist.Count; i++)
            {
                for (int j = i + 1; j < worklist.Count; j++)
                {
                    if (compaireIndWorkList(worklist[i], worklist[j])
                        && compaireReverseDepWorkList(worklist[i], worklist[j]))
                    {
                        error = "Эти работы имеет одинаковые начальные и конечные события:\n"
                            + (worklist[i]).Name + "\t" + (worklist[j]).Name
                            + ".\nДобавьте фиктивную работу.\n";
                    }
                }
            }
            return error;
        }
        
        /// <summary>
        /// Проверяет имеют ли две работы одинаковые начальные события  
        /// </summary>
        /// <param name="w1">Первая работа</param>
        /// <param name="w2">Вторая работа</param>
        /// <returns>true - работы исходят из одного события, false - из разных</returns>
        private bool compaireIndWorkList(Work w1, Work w2)
        {
            if (w1.DepInd.Count != w2.DepInd.Count)
            {
                return false;
            }

            if (w1.DepInd.Count == w2.DepInd.Count && w1.DepInd.Count == 0)
            {
                return true;
            }

            bool compaire = false;
            foreach (int i in w1.DepInd)
            {
                if (w2.DepInd.Contains(i))
                {
                    compaire = true;
                }
                else
                {
                    return false;
                }
            }
            return compaire;
        }

        private bool compaireReverseDepWorkList(Work w1, Work w2)
        {
            if (w1.ReversDepWorks.Count != w2.ReversDepWorks.Count)
            {
                return false;
            }

            bool compaire = false;
            foreach (Work work in w1.ReversDepWorks)
            {
                if (w2.ReversDepWorks.Contains(work))
                {
                    compaire = true;
                }
                else
                {
                    return false;
                }
            }
            return compaire;
        }//проверяет имеют ли две работы одинаковые конечные события
        #endregion

        #region ICloneable
        public Object Clone()
        {
            return new Project(this);
        }
        #endregion

        public int useRankSortRule(Work w1, Work w2)
        {
            if (w1.rank < w2.rank)
                return -1;
            else
                if (w1.rank == w2.rank)
                {
                    if (w1.timestart < w2.timestart)
                        return -1;
                    else
                        if (w1.timestart == w2.timestart)
                        {
                            if (w1.id < w2.id)
                                if (w2.Type == WorkType.Возврат)
                                    return 1;
                                else
                                    return -1;
                            else
                                return 0;
                        }
                        else
                            return 1;
                }
                else
                    return 1;
        }

    }
}
